package name.weily.liba.tools

import android.app.Activity
import android.app.ActivityOptions
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.util.TypedValue
import android.view.View
import java.io.File

inline fun <T : Any, R: Any> T.runSync(lock: Any = this, block: T.()-> R) : R {
    return synchronized(lock) {
        block()
    }
}


inline fun <R> runIfApiAfter(target: Int, equal: Boolean = true, afterTargetApi: () -> R) : R? {
    return if (equal) {
        if (Build.VERSION.SDK_INT >= target) {
            afterTargetApi()
        } else {
            null
        }
    } else {
        if (Build.VERSION.SDK_INT > target) {
            afterTargetApi()
        } else {
            null
        }
    }
}

inline fun <R> runIfApiAfter(target: Int, equal: Boolean = true, beforeTargetApi: () -> R, afterTargetApi: () -> R) : R {
    return if (equal) {
        if (Build.VERSION.SDK_INT >= target) {
            afterTargetApi()
        } else {
            beforeTargetApi()
        }
    } else {
        if (Build.VERSION.SDK_INT > target) {
            afterTargetApi()
        } else {
            beforeTargetApi()
        }
    }
}

/**
 * logging
 */
inline fun <reified T : Any> T.logv(message: String, tag: String = T::class.java.simpleName) = Log.v(tag, message)
inline fun <reified T : Any> T.logi(message: String, tag: String = T::class.java.simpleName) = Log.i(tag, message)
inline fun <reified T : Any> T.logd(message: String, tag: String = T::class.java.simpleName) = Log.d(tag, message)
inline fun <reified T : Any> T.logw(message: String, tag: String = T::class.java.simpleName) = Log.w(tag, message)
inline fun <reified T : Any> T.loge(message: String, tag: String = T::class.java.simpleName) = Log.e(tag, message)



/**
 * Activity
 */
inline fun <reified T> Activity.quickStartActivity(intent: Intent? = null, view: View) {
    val bundle = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.width, view.height).toBundle()
    intent?.apply { startActivity(this, bundle) } ?: startActivity(Intent(this, T::class.java), bundle)
}

inline fun <reified T> Activity.quickStartActivity(intent: Intent? = null, bundle: Bundle? = null) {
    intent?.apply { startActivity(this, bundle) } ?: startActivity(Intent(this, T::class.java), bundle)
}

inline fun <reified T> Activity.quickStartActivityForResult(intent: Intent? = null, requestCode: Int, bundle: Bundle? = null) {
    intent?.apply { startActivityForResult(this, requestCode, bundle) } ?: startActivityForResult(Intent(this, T::class.java), requestCode, bundle)
}

/**
 * Resource
 */
fun Context.getAttrRealRid(rid: Int) : Int {
    val typedValue = TypedValue()
    theme.resolveAttribute(rid, typedValue, true)
    return typedValue.data
}

fun Context.dp2px(dp: Int) : Int {
    return resources.displayMetrics.density.let {
        (dp * it + 0.5f).toInt()
    }
}

fun Context.px2dp(px: Int) : Int {
    return resources.displayMetrics.density.let {
        (px / it + 0.5f).toInt()
    }
}

/**
 * @param sp for font size
 */
fun Context.sp2px(sp: Int) : Int {
    return resources.displayMetrics.scaledDensity.let {
        (sp * it + 0.5f).toInt()
    }
}

fun Context.px2sp(px: Int) : Int {
    return resources.displayMetrics.scaledDensity.let {
        (px / it + 0.5f).toInt()
    }
}

/**
 * collections
 */

inline fun <T> List<T>.findIndexOf(t: T, cmp: (T, T) -> Boolean) : Int {
    var idx = -1
    forEachIndexed { index, item ->
        if (cmp(t, item)) {
            idx = index
            return@forEachIndexed
        }
    }
    return idx
}

inline fun <T> List<T>.findIndex(predictor: (T) -> Boolean) : Int {
    var idx = -1
    forEachIndexed { index, item ->
        if (predictor.invoke(item)) {
            idx = index
            return@forEachIndexed
        }
    }
    return idx
}

/**
 * implement of List.removeIf because of its api level limiting
 */
fun <T> MutableList<T>.removeAllIf(predictor: (T)-> Boolean) : Boolean {
    var removed = false
    synchronized(this) {
        iterator().apply {
            while (hasNext()) {
                if (predictor.invoke(next())) {
                    remove()
                    removed = true
                }
            }
        }
    }
    return removed
}


/**
 * @return directory path if this string is a file path, eg. /sdcard/aa/bb/cc.txt return "/sdcard/aa/bb/"
 */
fun String.basePath() : String {
    return this.let {
        val idx = it.lastIndexOf(File.separatorChar)
        if (idx > 0) it.substring(0, idx+1) else it
    }
}
